package agents;

import java.util.ArrayList;
import model.Model;
import model.info.Constants;
import model.mapping.MapPoint;
import agents.intelligence.AgentParticipantInfo;
import agents.intelligence.DriverMentalState;
import agents.relations.Vehicle;
import agents.resources.DeadDriverContentMessage;
import agents.resources.DriverResource;
import agents.resources.DriverUpdatePoints;
import aglobe.container.agent.Agent;
import aglobe.container.transport.InvisibleContainerException;
import aglobe.ontology.AgentInfo;
import aglobe.ontology.Message;
import aglobe.ontology.MessageConstants;

public class DriverAgent extends Agent {

	private DriverMentalState mentalState;
	private DriverResource resource;
	private Vehicle vehicle;
	private ArrayList<MapPoint> path;
	private Model model;
		
	public void init(AgentInfo ai, int initState) {
		
		mentalState = new DriverMentalState((AgentParticipantInfo)ai);
		System.out.println("Agent");
		System.out.println("Host: "+this.getAddress().getHost());
		System.out.println("Puerto: "+this.getAddress().getPort());
		System.out.println("Contenedor: "+this.getAddress().getContainerName());
		System.out.println("Nombre: "+this.getAddress().getName());
		System.out.println("AgentInfo");
		System.out.println("Clase principal: "+ai.getMainClass());
		System.out.println("Nombre: "+ai.getName());
		System.out.println("Nombre leible: "+ai.getReadableName());
		System.out.println("Tipo: "+ai.getType());
		System.out.println("Librerias: "+ai.getLibraries());
		System.out.println("Datos: "+ai.getData());
		System.out.println("");
		sendInitMessage();
	}

	public void handleIncomingMessage(Message message) {
	
		if (message.getPerformative().equals(MessageConstants.ACCEPT_PROPOSAL)) {
			finishExecution();
		}
		else if (message.getPerformative().equals(MessageConstants.INFORM)) {
			if (message.getContent() instanceof String) 
				if (message.getContent().equals(Constants.KILL))
					finishAsynchronousExecution();
		}
	}

	public DriverMentalState getMentalState() {
		return mentalState;
	}

	public void setMentalState(DriverMentalState mentalState) {
		this.mentalState = mentalState;
	}

	public DriverResource getResource() {
		return resource;
	}

	public void setResource(DriverResource resource) {
		this.resource = resource;
	}

	public Vehicle getVehicle() {
		return vehicle;
	}

	public void setVehicle(Vehicle vehicle) {
		this.vehicle = vehicle;
	}

	public void execDriverAgent(Model model,ArrayList<MapPoint> path) {
		
		this.path = path;
		this.model = model;
		vehicle = new Vehicle(model,Model.Vehicles.CAR.ordinal());
		resource = new DriverResource(model,vehicle.getTypeVehicle());
		DriverRunnable runnableDriver = new DriverRunnable();
		Thread driverThread = new Thread(runnableDriver);
		driverThread.start();
	}
	
	public void drive() {

		if (mentalState.getCurrentPlace() < path.size()) {
			MapPoint point = path.get(mentalState.getCurrentPlace());
			if (point.getOccupant().equals(Model.Individuals.FREE.ordinal())) {
				placeInPointVehicle(point);
				freePointVehicle();
				mentalState.setCurrentPlace(mentalState.getCurrentPlace() + 1);
				//System.out.println("Voy por: "+point.getMapPoint());
			}
		}
		else 
			mentalState.setDestinyReached(true);
	}
	
	public void placeInPointVehicle(MapPoint mapPoint) {
	
		vehicle.takePlace(mapPoint.x,mapPoint.y);
		resource.getOccupedMirrorPoints().add(mapPoint);
	}

	public void freePointVehicle() {
		
		vehicle.freePlace();
		if (!resource.getOccupedMirrorPoints().isEmpty())
			resource.getFreeMirrorPoints().add(resource.getOccupedMirrorPoints().remove(0));	
	}
	
	public void informVehiclePosition() {
		
		DriverUpdatePoints points = new DriverUpdatePoints(resource.getOccupedMirrorPoints(),
				resource.getFreeMirrorPoints());
		model.updateContent(11,points);
	}
	
	public Boolean situateVehicle() {
		
		Boolean result = false;
		MapPoint place = path.get(mentalState.getCurrentPlace());
		if (place.getOccupant().equals(Model.Individuals.FREE.ordinal())) {
			result = true;
			resource.getInitialPlace().add(place);
			placeInPointVehicle(place);
			mentalState.setCurrentPlace(mentalState.getCurrentPlace() + 1);
		}
		return result;
	}
	
	public void freeInitialPoint() {
		
		for (int i=0;i<resource.getInitialPlace().size();i++)
			resource.getInitialPlace().get(i).setOccupant(Model.Individuals.NONE.ordinal());
		this.scheduleEvent(new Runnable() {
			public void run() {
				for (int i=0;i<resource.getInitialPlace().size();i++)
					resource.getInitialPlace().get(i).setOccupant(Model.Individuals.FREE.ordinal());
				resource.getInitialPlace().clear();
			}
		},10*(500 - model.getSimulationSpeed()));
	}
	
	public void sendInitMessage() {
		
		TrafficAgent trafficAgent = (TrafficAgent) getContainer().getAgentManager().getAgentInstance(Constants.TRAFFIC_AGENT);
		if (trafficAgent.getLaunched()) {
			Message message = Message.newInstance();
			message.setPerformative(MessageConstants.INFORM_DONE);
			message.setSender(getAddress());
			message.setReceiver(trafficAgent.getAddress());
			message.setContent(getName());
			try {
				sendMessage(message);
			} catch (InvisibleContainerException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void sendDeadDoneMessage() {
		
		Message message = Message.newInstance();
		message.setPerformative(MessageConstants.DONE);
		message.setSender(getAddress());
		message.setReceiver(model.getTrafficAgent().getAddress());
		DeadDriverContentMessage content = new DeadDriverContentMessage(mentalState.getInfo(),vehicle.getTypeVehicle());
		message.setContent(content);
		try {
			sendMessage(message);
		} catch (InvisibleContainerException e) {
			e.printStackTrace();
		}
	}
	
	public void sendInformFinish() {
		
		Message message = Message.newInstance();
		message.setPerformative(MessageConstants.INFORM_RESULT);
		message.setSender(getAddress());
		message.setReceiver(model.getTrafficAgent().getAddress());
		message.setContent(mentalState.getInfo());
		try {
			sendMessage(message);
		} catch (InvisibleContainerException e) {
			e.printStackTrace();
		}
	}
	
	public void finishExecution() {
		
		try {
			freePointVehicle();
			sendDeadDoneMessage();
			informVehiclePosition();
			System.out.println(getName()+" muerto natural");
			kill();
			finish();
		} catch (Throwable e) {
			e.printStackTrace();
		}	
	}
	
	public void finishAsynchronousExecution() {
		
		freePointVehicle();
		informVehiclePosition();
		System.out.println(getName()+" muerto");
		kill();
		finish();
	}
	
	class DriverRunnable implements Runnable {
		
		public void run() {
			
			Boolean freeLane = false;
			do {
				freeLane = situateVehicle();
				if (!freeLane) {
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			} while (!freeLane);
			freeInitialPoint();
			informVehiclePosition();
			System.out.println("Situado");
			while (!mentalState.getDestinyReached()) {
				drive();
				informVehiclePosition();
				try {
					Thread.sleep(500 - model.getSimulationSpeed());
				} catch (Exception e) {
					e.printStackTrace();
				}
		    }
			sendInformFinish();
		}
	}
}
